﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using VsxFactory.Modeling.VisualStudio;
using System.IO;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio;

namespace VsxFactory.Modeling.Strategies.VisualStudio
{
    [System.Diagnostics.DebuggerDisplay("File : Project={Project.Name}, Name={Name}")]
    internal class VSProjectItem : IVsProjectFileItem
    {
        private HierarchyNode node;

        internal VSProjectItem(IVsProject project, HierarchyNode node) {
            System.Diagnostics.Contracts.Contract.Requires(project != null);
            System.Diagnostics.Contracts.Contract.Requires(node != null);
            this.Project = project;
            this.node = node;
        }

        public IVsProject Project {
            get;
            private set;
        }

        public IVsProjectFileItem AddFromTemplate(string templateName, string fileName) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(templateName));
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(fileName));

            var pi = node.ProjectItem.ProjectItems.AddFromTemplate(templateName, System.IO.Path.GetFileName(fileName));
            var hn = node.GetContainingProject().RecursiveFindByPath(pi.get_FileNames(0));
            return new VSProjectItem(Project, hn);
        }

        public IVsProjectFileItem AddFromFile(string relativeFileName) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(relativeFileName));

            node.EnsureCheckout();
            var pi = node.ProjectItem.ProjectItems.AddFromFile(relativeFileName);
            if (pi == null)
                return null;
            var hn = node.GetContainingProject().RecursiveFindByPath(pi.get_FileNames(0));
            return new VSProjectItem(Project, hn);
        }

        public string Name {
            get {
                return node.Name;
            }
            set {
                node.Name = value;
            }
        }

        public void RunCustomTool() {
            node.RunCustomTool();
        }

        public void Remove() {
            node.EnsureCheckout();
            node.Remove();
        }

        public void ReplaceText(string content, Encoding encoding = null) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(content));
            node.EnsureCheckout();
            IVsTextLines docData = node.GetDocData(Guid.Empty) as IVsTextLines;
            if (docData != null) {
                int endLine, endCol;
                docData.GetLastLineIndex(out endLine, out endCol);
                int len = (content == null) ? 0 : content.Length;
                IntPtr pText = System.Runtime.InteropServices.Marshal.StringToCoTaskMemAuto(content);
                try {
                    docData.ReplaceLines(0, 0, endLine, endCol, pText, len, null);
                    node.Save();
                }
                finally {
                    System.Runtime.InteropServices.Marshal.FreeCoTaskMem(pText);
                }
            }
            else {
                if (File.Exists(node.Path) ) {
                    encoding = Microsoft.VisualStudio.TextTemplating.EncodingHelper.GetEncoding(node.Path);
                }
                File.WriteAllText(node.Path, content, encoding ?? Encoding.Default);
            }
        }

        public object ExternalObject {
            get { return node; }
        }

        public string GetContent() {
            IVsTextLines docData = node.GetDocData(Guid.Empty) as IVsTextLines;
            if (docData != null) {
                return GetTextFromVsTextLines(docData);
            }
            return File.ReadAllText(node.Path);
        }

        public IVsSolution Solution {
            get { return Project.Solution; }
        }

        /// <summary>
        /// Gets the text from vs text lines.
        /// </summary>
        /// <param name="vsTextLines">The vs text lines.</param>
        /// <returns></returns>
        private static string GetTextFromVsTextLines(IVsTextLines vsTextLines) {
            System.Diagnostics.Contracts.Contract.Requires(vsTextLines != null);
            int num;
            int num2;
            string str;
            ErrorHandler.ThrowOnFailure(vsTextLines.GetLineCount(out num));
            ErrorHandler.ThrowOnFailure(vsTextLines.GetLengthOfLine(num - 1, out num2));
            ErrorHandler.ThrowOnFailure(vsTextLines.GetLineText(0, 0, num - 1, num2, out str));
            return str;
        }

        public string GetAttribute(string name) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(name));
            return node.GetAttribute(name);
        }

        public void SetAttribute(string name, string value) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(name));
            node.SetAttribute(name, value);
        }

        public IEnumerable<IVsProjectFileItem> AllElements {
            get { return node.Children.Select(n => new VSProjectItem(Project, n)); }
        }

        public bool GetAttributeAsBoolean(string name, bool defaultValue) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(name));
            return node.GetAttributeAsBoolean(name, defaultValue);
        }

        public T GetAttributeAsEnum<T>(string name, T defaultValue) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(name));
            return node.GetAttributeAsEnum<T>(name, defaultValue);
        }

        public string Path {
            get { return node.Path; }
        }

        public void Rename(string newName) {
            System.Diagnostics.Contracts.Contract.Requires(!String.IsNullOrWhiteSpace(newName));
            node.Rename(newName);
        }
    }
}
